package com.bigknow.appstore.proxy.handler.register;

import com.bigknow.appstore.proxy.cache.ICacheManager;
import com.bigknow.appstore.proxy.entity.ProxyResourceMeta;
import com.bigknow.appstore.proxy.enumerate.ErrorType;
import com.bigknow.appstore.proxy.exceptions.ProxyError;
import com.bigknow.appstore.proxy.handler.base.BaseInboundHandler;
import com.bigknow.appstore.proxy.log.ILogWriter;
import com.bigknow.appstore.proxy.server.IProxyServer;
import com.bigknow.appstore.proxy.util.HandlerVariables;
import com.bigknow.appstore.proxy.util.UriAndUrlHelper;
import com.bigknow.appstore.proxy.vo.RequestVo;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.http.FullHttpRequest;

import java.net.InetSocketAddress;
import java.util.Date;
import java.util.Map;

/**
 * 注册uri的类，如果url能够匹配上就在threadLocal中记录上requestVo,如果不能匹配记录
 * 就指直跳过供下一级handler处理
 * Created by chaos on 2016/9/19.
 */
@ChannelHandler.Sharable
public class UriRegistHandler extends BaseInboundHandler {

    public UriRegistHandler(IProxyServer proxyServer, ICacheManager cacheManager, ILogWriter writer) {
        super(proxyServer, cacheManager, writer);
    }

    @Override
    public void read(ChannelHandlerContext ctx, FullHttpMessage msg) throws Exception {
        FullHttpRequest request = (FullHttpRequest) msg;
        RequestVo requestVo = findresAndCreateRequestVoByCache(request.uri());
        /**
         * 如果为空就是构建访问错误的RequestVo对象
         * 添加错误的requestVo对象 errorCall 参数设置为true
         */
        if (requestVo == null) {
            requestVo = new RequestVo();
            requestVo.setCallUri(request.uri());//这个要设置原始uri
            requestVo.setCallTime(new Date());
            requestVo.setErrorCall(true);
        }
        requestVo.setCallIp(((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress());
        HandlerVariables.currentRequestVoLocal.set(requestVo);
        ctx.fireChannelRead(msg);
    }

    /**
     * 根据uri查询缓存，看uri请求是否是resource资源中的一个，如果是
     * 就构建requestVO并返回，如果不是就返回null
     * uri判断逻辑分为两步：
     * 1.判断uri是否直接能够匹配，如果不能继续第2步；
     * 2.判断uri前置是否能够匹配，如果不能就返回null；
     * <p/>
     * 当uri各种不符合规范时会内部抛出异常，但异常会被捕获并返回 null；
     *
     * @param uri
     * @return
     */
    private RequestVo findresAndCreateRequestVoByCache(String uri) {
        try {
            boolean rootUri = false;
            //1.判断uri是否直接能够匹配，如果不能继续第2步；
            ProxyResourceMeta resourceMeta = getProxyResourceCache()
                    .getProxyResourceMetaByUri(UriAndUrlHelper.getStandardUri(uri));
            //2.判断uri前置部分是否能够匹配，如果不能就返回null；
            if (resourceMeta == null) {
                String prefixUri = UriAndUrlHelper.getPrefixUri(uri);
                if (prefixUri == null) {
                    logger.info("路径不标准如果开启lastRequest将进一步进行补全处理：uri：[{}]", uri);
                    return null;
                }
                resourceMeta = getProxyResourceCache().getProxyResourceMetaByUri(prefixUri);
            } else {
                rootUri = true;//如果方位路径是初始路径就为true
            }
            if (resourceMeta == null) return null;

            RequestVo requestVo = new RequestVo();
            requestVo.setCallUri(uri);//这个要设置原始uri
            requestVo.setCallTime(new Date());
            requestVo.setRootUri(rootUri);
            requestVo.setPrefixUri(resourceMeta.getProxyUri());//前缀uri就是注册的服务uri
            //后置uri，就是请求的路径和参数,这里的后缀是加上了资源resource path的前缀
            requestVo.setSuffixUri(resourceMeta.getSourcePath() + UriAndUrlHelper.getSuffixUri(uri));
            requestVo.setResourceMeta(resourceMeta);
            return requestVo;
        } catch (Exception e) {
            //e.printStackTrace();
            logger.info("错误的访问  uri: [{}]！", uri);
            return null;
        }
    }
}
